package com.aaron.nettystudy.server;

import com.aaron.nettystudy.coding.KyroDecoder;
import com.aaron.nettystudy.coding.KyroEncoder;
import com.aaron.nettystudy.entity.RpcRequest;
import com.aaron.nettystudy.entity.RpcResponse;
import com.aaron.nettystudy.handler.NettyServerHandler;
import com.aaron.nettystudy.serialize.KyroSerializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName ServerClient
 * @description:
 * @author: 荣燊
 * @create: 2021-05-14 22:23
 **/
public class NettyServer {
    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private final int port;

    private NettyServer(int port) {
        this.port = port;
    }

    private void run() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        KyroSerializer kryoSerializer = new KyroSerializer();

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    // TCP默认开启了 Nagle 算法，该算法的作用是尽可能的发送大数据快，减少网络传输。TCP_NODELAY 参数的作用就是控制是否启用 Nagle 算法。
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    // 是否开启 TCP 底层心跳机制
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //表示系统用于临时存放已完成三次握手的请求的队列的最大长度,如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            //入栈逻辑，将字节转化为对象（KyroDecoder内部使用的是反序列化方法），再传输给下一个入栈处理逻辑。
                            //这里是客户端给服务端发送的消息，所以要转化为RpcRequest格式
                            ch.pipeline().addLast(new KyroDecoder(kryoSerializer, RpcRequest.class));
                            //出栈逻辑，将对象转化为字节（KyroEncoder内部使用的是序列化方法）。放入到ByteBuf中
                            ch.pipeline().addLast(new KyroEncoder(kryoSerializer, RpcResponse.class));
                            //入栈逻辑，再做自定义的Handler，得到第一个入栈传过来的数据。
                            //先log出来收到客户端的请求消息（RpcRequest格式），之后再回复一个消息（放到ctx上下文上，消息格式是RpcResponse）
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });

            // 绑定端口，同步等待绑定成功
            ChannelFuture f = b.bind(port).sync();

            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            logger.error("occur exception when start server:", e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) {
        new NettyServer(8889).run();
    }

}
